package com.luych.oauth2Center.service;

import com.luych.common.constant.SpringCacheConstant;
import com.luych.common.constant.SpringStoreConstant;
import com.luych.common.page.Page;
import com.luych.oauth2Center.config.Oauth2Config;
import com.luych.oauth2Center.dao.Oauth2ClientMapper;
import com.luych.oauth2Center.dao.Oauth2ScopeMapper;
import com.luych.oauth2Center.entity.*;
import com.luych.oauth2Center.modal.Oauth2Code;
import com.luych.oauth2Center.modal.Oauth2Token;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class Oauth2Service {

    private final Oauth2ClientMapper oauth2ClientMapper;
    private final Oauth2ScopeMapper oauth2ScopeMapper;
    private final RedisConnectionFactory redisConnectionFactory;
    private final Oauth2Config oauth2Config;

    @Autowired
    public Oauth2Service(Oauth2ClientMapper oauth2ClientMapper, Oauth2ScopeMapper oauth2ScopeMapper, RedisConnectionFactory redisConnectionFactory, Oauth2Config oauth2Config) {
        this.oauth2ClientMapper = oauth2ClientMapper;
        this.oauth2ScopeMapper = oauth2ScopeMapper;
        this.redisConnectionFactory = redisConnectionFactory;
        this.oauth2Config = oauth2Config;
    }

    public Page pageClient(String keywords, Page page) {
        Oauth2ClientExample example = new Oauth2ClientExample();
        if (!StringUtils.isEmpty(keywords)) {
            example.or().andClientIdLike(keywords);
            example.or().andRedirectUrlLike(keywords);
        }
        page.setRecordCnt(oauth2ClientMapper.countByExample(example));
        example.setOffset(page.getPageStart());
        example.setLimit(page.getPageSize());
        if (!StringUtils.isEmpty(page.getSortName()) && !StringUtils.isEmpty(page.getSortOrder())) {
            example.setOrderByClause(page.getSortName() + " " + page.getSortOrder());
        }
        page.setRecords(oauth2ClientMapper.selectByExample(example));
        return page;
    }

    @Cacheable(cacheNames = SpringCacheConstant.oauth2Server_client, key = "#oauth2Client.clientId")
    public Oauth2Client addClient(Oauth2Client oauth2Client) {
        oauth2ClientMapper.insertSelective(oauth2Client);
        return oauth2ClientMapper.selectByPrimaryKey(oauth2Client.getClientId());
    }

    @CachePut(cacheNames = SpringCacheConstant.oauth2Server_client, key = "#oauth2Client.clientId")
    public Oauth2Client modifyClient(Oauth2Client oauth2Client) {
        oauth2ClientMapper.updateByPrimaryKey(oauth2Client);
        return oauth2ClientMapper.selectByPrimaryKey(oauth2Client.getClientId());
    }

    @CacheEvict(cacheNames = SpringCacheConstant.oauth2Server_client, key = "#clientId")
    public void removeClient(String clientId) {
        oauth2ClientMapper.deleteByPrimaryKey(clientId);
    }

    @Cacheable(cacheNames = SpringCacheConstant.oauth2Server_client, key = "#clientId")
    public Oauth2Client getByClientId(String clientId) {
        return oauth2ClientMapper.selectByPrimaryKey(clientId);
    }

    public List<Oauth2Scope> getScopeByClientId(String clientId) {
        Oauth2Client oauth2Client = getByClientId(clientId);
        String scopeIds = oauth2Client.getGrantScopeIds();
        if (StringUtils.isEmpty(scopeIds)) {
            return new ArrayList<>(0);
        }
        List<Oauth2Scope> scopeList = new ArrayList<>();
        for (String scopeId : scopeIds.split(",")) {
            scopeList.add(oauth2ScopeMapper.selectByPrimaryKey(scopeId));
        }
        return scopeList;
    }

    public String getScopeCodeByClientId(String clientId) {
        List<Oauth2Scope> scopeList = getScopeByClientId(clientId);
        StringBuilder stringBuilder = new StringBuilder();
        for (Oauth2Scope scope : scopeList) {
            stringBuilder.append(scope).append(",");
        }
        return stringBuilder.substring(0, stringBuilder.length() - 1);
    }

    public List<Oauth2Scope> listScope() {
        return oauth2ScopeMapper.selectByExample(new Oauth2ScopeExample());
    }

    public void saveToken(String clientId, String accessToken, String refreshToken, String scope) {
        Oauth2Token oauth2Token = new Oauth2Token();
        oauth2Token.setClientId(clientId);
        oauth2Token.setAccessToken(accessToken);
        oauth2Token.setRefreshToken(refreshToken);
        oauth2Token.setScope(scope);
        oauth2Token.setExpire(oauth2Config.getToken().getExpire());

        String redisKey = SpringStoreConstant.oauth2_token + "_" + clientId;
        RedisTemplate<String, Oauth2Token> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.boundValueOps(redisKey).set(oauth2Token, oauth2Config.getToken().getExpire(), TimeUnit.SECONDS);
    }

    public Oauth2Token getToken(String clientId) {
        String redisKey = SpringStoreConstant.oauth2_token + "_" + clientId;
        RedisTemplate<String, Oauth2Token> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        return redisTemplate.boundValueOps(redisKey).get();
    }

    public void saveAuthenticationCode(String clientId, String userId, String code, String redirectUri, String scope) {
        Oauth2Code oauth2Code = new Oauth2Code();
        oauth2Code.setClientId(clientId);
        oauth2Code.setUserId(userId);
        oauth2Code.setCode(code);
        oauth2Code.setRedirectUrl(redirectUri);
        oauth2Code.setScope(scope);
        oauth2Code.setExpire(oauth2Config.getCode().getExpire());

        String redisKey = SpringStoreConstant.oauth2_code + "_" + clientId + "_" + code;
        RedisTemplate<String, Oauth2Code> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.boundValueOps(redisKey).set(oauth2Code, oauth2Config.getCode().getExpire(), TimeUnit.SECONDS);
    }

    public Oauth2Code getAuthenticationCode(String clientId, String code) {
        String redisKey = SpringStoreConstant.oauth2_code + "_" + clientId + "_" + code;
        RedisTemplate<String, Oauth2Code> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        return redisTemplate.boundValueOps(redisKey).get();
    }

    public void deleteAuthenticationCode(String clientId, String code) {
        String redisKey = SpringStoreConstant.oauth2_code + "_" + clientId + "_" + code;
        RedisTemplate<String, Oauth2Code> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.delete(redisKey);
    }
}
